﻿using System;
using System.Collections.Generic;
using System.Text;

using System.Collections;

namespace knetcat
{
    public enum argumentStart { argument_no, argument_required , argument_option };

    public class clsOptions
    {
        private string _Name=string.Empty;
        /// <summary>
        /// 这个选项的名称，就是一个字符串啦,空值代表没有-A --A之类的东西
        /// </summary>
        public string name
        {
            get { return _Name; }
            set { _Name = value; }
        }

        private argumentStart _argStart=argumentStart.argument_no;

        public argumentStart argStart
        {
            get { return _argStart; }
            set { _argStart = value; }
        }

        private string _opts=string.Empty;
        /// <summary>
        /// 这个选项的值
        /// </summary>
        public string opts
        {
            get { return _opts; }
            set { _opts = value; }
        }


    }

    /// <summary>
    /// 这个类是从命令行参数中读取信息然后转变成程序能懂的格式
    /// </summary>
    public  class clsGetOpts
    {
        /// <summary>
        /// 短的选项
        /// </summary>
        clsOptions [] _shortOptions;

        /// <summary>
        /// 长的选项
        /// </summary>
        clsOptions[] _longOptions;

        /// <summary>
        /// 这个是最后取得的选项
        /// </summary>
        clsOptions[] _Options;

        private int _optCount;

        public int optCount
        {
            get { return _optCount; }
            set { _optCount = value; }
        }
        

        public string setArgs(string[] args, string shortOptions, clsOptions[] longOptions)
        {
            string str = setOptions(shortOptions,longOptions);
            if (str!=string.Empty)
            {
                return str;
            }
            str = setArgs(args);
            if (str != string.Empty)
            {
                return str;
            }
            return string.Empty;
        }

        public string setOptions(string shortOptions , clsOptions [] longOptions)
        {
            //长的选项直接赋值就可以了吧,我这里用深度拷贝
            if (longOptions!=null)
            {
                _longOptions = (clsOptions[])longOptions.Clone();
            }

            if (shortOptions.Length==0)
            {
                return string.Empty;
            }

            //如下是处理短的选项.因为不知道有多少个选项，我这里先用动态数组
            ArrayList arrlistShortOptions = new ArrayList();

            for (int i = 0; i < shortOptions.Length; i++ )
            {
                //建立一个选项对象
                clsOptions opt = new clsOptions();
                opt.name = shortOptions[i].ToString();//名称就是这个字符啦
                //还得判断这个是否是字符或者数字呢
                if (! char.IsLetterOrDigit(shortOptions[i]))
                {
                    //如果不是字符或者数字就直接返回错误啦，并且清空选项
                    _shortOptions = null;
                    _longOptions = null;
                    return "遇到不能识别选项，非字母或数字:"+opt.name;

                }
                //接下来判断
                if ((i==shortOptions.Length-1)//如果这个是最后一位
                    ||(char.IsLetterOrDigit(shortOptions[i+1])))//或者下一位是字母或者数字
                {
                    opt.argStart = argumentStart.argument_no;//就不需要参数啦
                }else if ((i<shortOptions.Length-2)//起码长度能够允许“：：”吧
                    &&(shortOptions.Substring(i+1,2)=="::"))//截取字符串判断是否是“：：”
                {
                    opt.argStart = argumentStart.argument_option;
                    i += 2;//因为多了2个冒号
                }
                else //这种情况只能是有一个“：”了
                {
                    //还得判断这个字符到底是不是个字符啊
                    opt.argStart = argumentStart.argument_required;
                    i += 1;//因为多了一个冒号
                }

                //然后将这个选项加进那个动态数组
                arrlistShortOptions.Add(opt);
            }
            //然后将这个动态数组加转换成数组
            _shortOptions = new clsOptions[arrlistShortOptions.Count];
            arrlistShortOptions.CopyTo(_shortOptions);

            return string.Empty;

        }

        

        public clsOptions getOptions(int index)
        {
            if (index<_Options.Length)
            {
                return _Options[index];
            }
            return null;
        }

        /// <summary>
        /// 设置参数的，就是那个args
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        public string setArgs(string []args)
        {
            //如下是处理这些参数，依旧是迭代处理，还有逗号是特别处理的

            //因为不知道有多少个选项，我这里还是用动态数组来实现
            ArrayList arrlistOptions = new ArrayList();
            //首先将这个字符串拷贝
            //我这里用深度拷贝,因为有些选项像-ABC，一个“-”后跟着3个呢，或者也可以用index之类的来实现这个
            //string[] arrArgs = (string[])args.Clone();

            for (int i = 0; i < args.Length; i++)
            {
                //首先判断第一个字符是否是"-",如果是，就
                if (args[i][0]=='-')
                {

                    //再判断是否是2个“--”的
                    if ((args[i].Length>2)//“--”都占了2个长度啦
                        && (args[i][1] == '-'))//第二个字符是否是“-”
                    {
                        //这里是跟长选项判断
                        //这里是跟短选项判断
                        //截取字符串
                        
                        for (int j = 2; j < args.Length; j++) //因为前面已经有一个“-”，所以从1开始
                        {
                            //首选选则一个字符，判断是否在短选项中
                            string str = args[i].Substring(2);
                            clsOptions opt = searchOptions(str, _longOptions);

                            //然后加上j
                            j += opt.name.Length;

                            if (opt == null)
                            {
                                return "字符不能识别是什么选项：" + str;
                            }
                            //再判断这个是否需要参数
                            if (opt.argStart == argumentStart.argument_required)
                            {
                                //如果需要参数，就判断这个字符是否是这个字符串的最后一项，
                                //如果不是最后一项，就直接将剩下的作为参数啦
                                //如果是最后一项，就将下一项作为参数啦
                                opt.opts = getCanShu(args, ref i, j);
                                if (opt.opts == string.Empty)
                                {
                                    return "-" + opt.name + "应该有参数";
                                }
                                

                            }
                            else if (opt.argStart == argumentStart.argument_option)//参数可有可无的例子
                            {
                                opt.opts = getCanShu(args, ref i, j);//用这个取得参数，如果没有参数，就是空值
                            }

                            arrlistOptions.Add(opt);

                        }

                    }
                    else
                    {
                        //这里是跟短选项判断
                        //截取字符串
                        //string strTemp = args[i].Substring(1);//这个是为了应对"-abc"这种情况
                        for (int j = 1; j < args[i].Length;j++ ) //因为前面已经有一个“-”，所以从1开始
                        {
                            //System.Console.WriteLine(i.ToString() + ";" + j.ToString()+":"+args[i]);
                            string str = args[i][j].ToString();
                            //首选选则一个字符，判断是否在短选项中
                            clsOptions opt = searchOptions(str, _shortOptions);

                            if (opt==null)
                            {
                                return "字符不能识别是什么选项："+str.ToString();
                            }
                            //再判断这个是否需要参数
                            if (opt.argStart==argumentStart.argument_required)
                            {
                                //如果需要参数，就判断这个字符是否是这个字符串的最后一项，
                                //如果不是最后一项，就直接将剩下的作为参数啦
                                //如果是最后一项，就将下一项作为参数啦
                                opt.opts = getCanShu(args, ref i, j+1);
                                if (opt.opts==string.Empty)
                                {
                                    return "-"+opt.name+"应该有参数";
                                }
                                
                                    
                            }else if ( opt.argStart==argumentStart.argument_option)//参数可有可无的例子
                            {
                                opt.opts = getCanShu(args, ref i, j + 1);//用这个取得参数，如果没有参数，就是空值


                            }

                            arrlistOptions.Add(opt);
                        }

                    }

                }
                else
                {
                    //如下的就是没有“-”开头的
                    clsOptions opt = new clsOptions();
                    opt.name = string.Empty;// 空值
                    opt.argStart = argumentStart.argument_required;//当然是有个参数啦
                    opt.opts = args[i];
                    arrlistOptions.Add(opt);
                }

            }

            _Options = new clsOptions[arrlistOptions.Count];
            arrlistOptions.CopyTo(_Options);//转换成数组
            optCount = arrlistOptions.Count;

            return string.Empty;
        }


        /// <summary>
        /// 这个是取得参数的
        /// </summary>
        /// <param name="args"></param>
        /// <param name="args_index"></param>
        /// <param name="arg_index"></param>
        /// <param name="addi"></param>
        /// <returns></returns>
        private string getCanShu(string[] args, ref int args_index, int arg_index)
        {
            string strCanShu = string.Empty;

            int i= args_index;

            //如果这一项不是结尾，就先加上
            if (arg_index<args[i].Length-1)
            {
                strCanShu += args[i].Substring(arg_index);
            }
            else
            {
                if ((i==args.Length-1)//后边没有参数啦
                    ||(args[i+1].StartsWith("-")))//或者下一项的开头是“-”,应对那个没参数的那个

                {
                    return string.Empty;
                }

                strCanShu += args[++i];//直接加上下一项

            }

            //接下来判断是否有,号吧
            while(i<args.Length)
            {
                i++;

                //args_index + addi就是已经加到strReturn中的那一项
                //判断这一项的结尾是否是",",或者下一项开头是","
                if (args[i] == ",") //如果这一项是“，”
                {
                    strCanShu += ",";
                    i += 2;
                    strCanShu += args[i];

                }
                else if ((args[i ].StartsWith(","))//如果开头是“，”
                    ||(args[i-1].EndsWith(",")))//或者前一项的结尾是
                {
                    strCanShu += args[i];//加上项
                }
                else
                {
                    
                    break;//其他情况就是退出循环了
                    
                }
            }

            args_index = i-1;//从最后一项倒退
            return strCanShu;

        }

        /// <summary>
        /// 根据一个字符串，来查找是否有相关的选项
        /// </summary>
        /// <param name="s"></param>
        /// <param name="opts"></param>
        /// <returns></returns>
        private clsOptions searchOptions(string s, clsOptions [] arropts)
        {
            ArrayList arrlist = new ArrayList();
            //这个是为了匹配不同大小的而准备的，所以
            foreach (clsOptions item in arropts)
            {
                if (item.name==s.Substring(0,item.name.Length))
                {
                    //深度赋值，新建一个
                    clsOptions opt = new clsOptions();
                    opt.name = item.name;
                    opt.argStart = item.argStart;
                    opt.opts = item.opts;
                    arrlist.Add(opt);
                }
            }
            if (arrlist.Count==1)
            {
                return (clsOptions)arrlist[0];//只有一个匹配的就返回这个
            }else if (arrlist.Count>1)
            {
                //返回字符最高的那个
                clsOptions tmp = (clsOptions)arrlist[0];
                for (int i = 1; i < arrlist.Count;i++ )
                {
                    if (((clsOptions)arrlist[i - 1]).name.Length < ((clsOptions)arrlist[i ]).name.Length)
                    {
                        tmp = (clsOptions)arrlist[i];
                    }
                }
                return tmp;
            }

            
            return null;
        }
    }
}
